package hcs.interfaces;


import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Observable;
import java.util.Observer;
import java.util.TreeMap;
import java.util.Vector;

public abstract class PriorityObservable extends Observable {

	private Map<Integer,List<Observer>> orderedQueue;
	public abstract int getPriority(Object o);
	
	public PriorityObservable() {
		orderedQueue = new TreeMap<Integer, List<Observer>>();
	}

	public synchronized void addObserver(Observer o) {
        if (o == null) {
            throw new NullPointerException();
        }
        List<Observer> allObs = getAllObservers();
        if (!allObs.contains(o)) {
        	int priority = getPriority(o);			
        	if (orderedQueue.containsKey(priority)) {
        		List<Observer> curList = orderedQueue.get(priority);
        		curList.add(o);
        	} else {
        		List<Observer> newList = new Vector<Observer>();
        		newList.add(o);
        		orderedQueue.put(priority, newList);
        	}
        }
    }
	
	private List<Observer> getAllObservers() {
		List<Observer> result = new Vector<Observer>();
		Iterator<List<Observer>> allLists = orderedQueue.values().iterator();
		while (allLists.hasNext()) {
			List<Observer> aList= allLists.next();
			result.addAll(aList);
		}
		
		return result;
	}

	public synchronized void deleteObserver(Observer o) {
		Iterator<List<Observer>> allLists = orderedQueue.values().iterator();
		while (allLists.hasNext()) {
			List<Observer> aList = allLists.next();
			if (aList.contains(o)) {
				aList.remove(o);
				break;
			}
		}
	}
	
	public void notifyObservers(Object arg) {

		/*
         * a temporary array buffer, used as a snapshot of the state of
         * current Observers.
         */
        Object[] arrLocal;
		
		synchronized (this) {
			/* We don't want the Observer doing callbacks into
			 * arbitrary code while holding its own Monitor.
			 * The code where we extract each Observable from 
			 * the Vector and store the state of the Observer
			 * needs synchronization, but notifying observers
			 * does not (should not).  The worst result of any 
			 * potential race-condition here is that:
			 * 1) a newly-added Observer will miss a
			 *   notification in progress
			 * 2) a recently unregistered Observer will be
			 *   wrongly notified when it doesn't care
			 */
			if (!hasChanged()) {
				return;
			}
			arrLocal = getAllObservers().toArray();
			clearChanged();
		}

		for (int i = 0; i<arrLocal.length; i++)
			((Observer)arrLocal[i]).update(this, arg);
	}
	
	public synchronized void deleteObservers() {
		orderedQueue.clear();
	    }
	
	public synchronized int countObservers() {
		return getAllObservers().size();
	    }
}
